package com.yf.system.modules.user.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yf.ability.captcha.service.CaptchaService;
import com.yf.ability.excel.ExportExcel;
import com.yf.ability.excel.ImportExcel;
import com.yf.ability.log.annon.LogInject;
import com.yf.ability.log.enums.LogType;
import com.yf.ability.redis.service.RedisService;
import com.yf.ability.shiro.dto.SysUserLoginDTO;
import com.yf.base.api.annon.DataProtect;
import com.yf.base.api.api.ApiRest;
import com.yf.base.api.api.controller.BaseController;
import com.yf.base.api.api.dto.*;
import com.yf.base.api.exception.ServiceException;
import com.yf.base.utils.ResourceUtil;
import com.yf.notify.enums.MsgTmpl;
import com.yf.notify.modules.notify.dto.request.MsgReceiverDTO;
import com.yf.notify.modules.notify.service.MsgService;
import com.yf.notify.modules.sms.utils.SmsUtils;
import com.yf.system.modules.user.dto.request.*;
import com.yf.system.modules.user.dto.response.FaceCheckRespDTO;
import com.yf.system.modules.user.dto.response.UserExportDTO;
import com.yf.system.modules.user.dto.response.UserListRespDTO;
import com.yf.system.modules.user.entity.SysUser;
import com.yf.system.modules.user.enums.LoginType;
import com.yf.system.modules.user.enums.SmsType;
import com.yf.system.modules.user.enums.UserState;
import com.yf.system.modules.user.service.SysUserBindService;
import com.yf.system.modules.user.service.SysUserRoleService;
import com.yf.system.modules.user.service.SysUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 管理用户控制器
 * </p>
 *
 * @author 聪明笨狗
 * @since 2020-04-13 16:57
 */
@Api(tags = {"管理用户"})
@RestController
@Log4j2
@RequestMapping("/api/sys/user")
public class SysUserController extends BaseController {

    @Autowired
    private SysUserService baseService;

    @Autowired
    private SysUserRoleService sysUserRoleService;

    @Autowired
    private SysUserBindService sysUserBindService;

    @Autowired
    private ExportExcel exportExcel;

    @Autowired
    private MsgService msgService;

    @Autowired
    private CaptchaService captchaService;

    @Autowired
    private RedisService redisService;

    /**
     * 300秒有效期
     */
    private static final Long SMS_EXPIRY = 300L;




    /**
     * 用户详情
     *
     * @return
     */
    @RequiresPermissions(value = {"sys:user:list"})
    @ApiOperation(value = "用户详情")
    @PostMapping("/detail")
    public ApiRest<SysUserSaveReqDTO> detail(@RequestBody BaseIdReqDTO reqDTO) {
        SysUserSaveReqDTO respDTO = baseService.detail(reqDTO.getId());
        return super.success(respDTO);
    }

    /**
     * 用户登录
     *
     * @return
     */
    @LogInject(title = "账号密码登录", logType = LogType.LOGIN)
    @ApiOperation(value = "账号密码登录")
    @PostMapping("/login")
    public ApiRest<SysUserLoginDTO> login(@RequestBody SysUserLoginReqDTO reqDTO) {
        SysUserLoginDTO respDTO = baseService.login(reqDTO);
        return super.success(respDTO);
    }

    /**
     * 游客登录
     *
     * @return
     */
    @LogInject(title = "账号密码登录", logType = LogType.LOGIN)
    @ApiOperation(value = "账号密码登录")
    @PostMapping("/visitorLogin")
    public ApiRest<SysUserLoginDTO> visitorLogin(@RequestBody SysUserLoginReqDTO reqDTO) {
        SysUserLoginDTO respDTO = baseService.visitorLogin(reqDTO);
        return super.success(respDTO);
    }

    /**
     * 用户登录
     *
     * @return
     */
    @LogInject(title = "手机号登录", logType = LogType.LOGIN)
    @ApiOperation(value = "手机号登录")
    @PostMapping("/mobile-login")
    public ApiRest<SysUserLoginDTO> mobileLogin(@RequestBody MobileLoginReqDTO reqDTO) {
        SysUserLoginDTO respDTO = baseService.mobileLogin(reqDTO);
        return super.success(respDTO);
    }

    /**
     * 人脸登录
     *
     * @return
     */
    @LogInject(title = "通过人脸认证登录", logType = LogType.LOGIN)
    @ApiOperation(value = "人脸登录")
    @PostMapping("/face-login")
    public ApiRest<SysUserLoginDTO> faceLogin(@RequestBody FaceLoginReqDTO reqDTO) {
        SysUserLoginDTO respDTO = baseService.faceLogin(reqDTO);
        return super.success(respDTO);
    }

    /**
     * 用户登录
     *
     * @return
     */
    @ApiOperation(value = "退出登录")
    @PostMapping("/logout")
    public ApiRest logout(HttpServletRequest request) {
        String token = request.getHeader("token");
        baseService.logout(token);
        return super.success();
    }

    /**
     * 获取会话
     *
     * @return
     */
    @ApiOperation(value = "获取会话")
    @PostMapping("/info")
    public ApiRest info(@RequestBody BaseTokenReqDTO reqDTO) {
        SysUserLoginDTO respDTO = baseService.token(reqDTO.getToken());
        return success(respDTO);
    }

    /**
     * 修改用户资料
     *
     * @return
     */
    @DataProtect(clazz = SysUser.class, update = true, currUsr = true)
    @ApiOperation(value = "修改用户资料")
    @PostMapping("/update")
    public ApiRest update(@RequestBody SysUserUpdateReqDTO reqDTO) {
        baseService.update(reqDTO);
        return success();
    }

    /**
     * 修改密码
     *
     * @return
     */
    @DataProtect(clazz = SysUser.class, update = true, currUsr = true)
    @ApiOperation(value = "修改密码")
    @PostMapping("/update-pass")
    public ApiRest updatePass(@RequestBody SysUserPassReqDTO reqDTO) {
        baseService.pass(reqDTO);
        return success();
    }



    /**
     * 保存或修改系统用户
     *
     * @return
     */
    @RequiresPermissions(value = {"sys:user:add", "sys:user:update"}, logical = Logical.OR)
    @DataProtect(clazz = SysUser.class, update = true)
    @ApiOperation(value = "保存或修改")
    @PostMapping("/save")
    public ApiRest save(@RequestBody SysUserSaveReqDTO reqDTO) {
        baseService.save(reqDTO);
        return success();
    }


    /**
     * 批量删除
     *
     * @param reqDTO
     * @return
     */
    @RequiresPermissions(value = {"sys:user:delete"})
    @DataProtect(clazz = SysUser.class, delete = true)
    @ApiOperation(value = "批量删除")
    @PostMapping("/delete")
    public ApiRest edit(@RequestBody BaseIdsReqDTO reqDTO) {
        //根据ID删除
        baseService.delete(reqDTO.getIds());
        return super.success();
    }

    /**
     * 分页查找
     *
     * @param reqDTO
     * @return
     */
    @RequiresPermissions(value = {"sys:user:list"})
    @ApiOperation(value = "分页查找")
    @PostMapping("/paging")
    public ApiRest<IPage<UserListRespDTO>> paging(@RequestBody PagingReqDTO<SysUserQueryReqDTO> reqDTO) {

        //分页查询并转换
        IPage<UserListRespDTO> page = baseService.paging(reqDTO);
        return super.success(page);
    }

    /**
     * 修改状态
     *
     * @param reqDTO
     * @return
     */
    @RequiresPermissions(value = {"sys:user:state"})
    @ApiOperation(value = "修改状态")
    @PostMapping("/state")
    public ApiRest state(@RequestBody BaseStateReqDTO reqDTO) {

        // 条件
        QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
        wrapper.lambda()
                .in(SysUser::getId, reqDTO.getIds())
                .ne(SysUser::getUserName, "admin");

        SysUser record = new SysUser();
        record.setState(reqDTO.getState());
        baseService.update(record, wrapper);
        return super.success();
    }

    /**
     * 用户注册
     * @return
     */
    @ApiOperation(value = "用户注册", notes = "通过账号密码注册")
    @PostMapping("/reg")
    public ApiRest<SysUserLoginDTO> reg(@RequestBody UserRegReqDTO reqDTO) {
        SysUserLoginDTO respDTO = baseService.reg(reqDTO);

        // 待审核的状态
        if(UserState.AUDIT.equals(respDTO.getState())){
            throw new ServiceException("注册成功，管理员审核后方可登录！");
        }
        return success(respDTO);
    }

    /**
     * 批量修改部门
     *
     * @return
     */
    @RequiresPermissions(value = {"sys:user:batch-dept"})
    @ApiOperation(value = "批量修改部门")
    @PostMapping("/batch-dept")
    public ApiRest batchDept(@RequestBody UserDeptReqDTO reqDTO) {
        baseService.batchDept(reqDTO);
        return success();
    }

    /**
     * 批量修改角色
     *
     * @return
     */
    @RequiresPermissions(value = {"sys:user:batch-role"})
    @ApiOperation(value = "批量修改角色")
    @PostMapping("/batch-role")
    public ApiRest batchRole(@RequestBody UserRoleReqDTO reqDTO) {
        sysUserRoleService.batchRole(reqDTO);
        return success();
    }

    /**
     * 重置密码
     *
     * @return
     */
    @ApiOperation(value = "重置密码")
    @PostMapping("/reset-pass")
    public ApiRest resetPass(@RequestBody ResetPassReqDTO reqDTO) {
        baseService.resetPass(reqDTO);
        return success();
    }


    /**
     * 导出excel文件
     */
    @RequiresPermissions(value = {"sys:user:export"})
    @PostMapping("/export")
    public ApiRest exportFile(HttpServletResponse response, @RequestBody SysUserQueryReqDTO reqDTO) {


        System.out.println("++++查找导出数据");
        List<UserExportDTO> list = baseService.listForExport(reqDTO);

        // 导出数据
        exportExcel.export(response, UserExportDTO.class, "用户列表", list);

        return super.success();
    }


    /**
     * 导入Excel
     *
     * @param file
     * @return
     */
    @RequiresPermissions(value = {"sys:user:import"})
    @PostMapping("import")
    public ApiRest importFile(@RequestParam("file") MultipartFile file) {

        ImportExcel excel = new ImportExcel(file);
        List<UserExportDTO> list = excel.getDataList(UserExportDTO.class);

        // 导入数据条数
        baseService.importExcel(list);

        // 导入成功
        return super.success();
    }


    /**
     * 下载导入题目数据模板
     */
    @RequiresPermissions(value = {"sys:user:import"})
    @PostMapping("import/template")
    public ApiRest importFileTemplate(HttpServletResponse response) {
        try {

            // 读出文件流
            ResourceUtil.write(response, "excel/user_template.xlsx");

            return super.success();
        } catch (Exception e) {
            e.printStackTrace();
            return super.failure("导入模板下载失败！失败信息：" + e.getMessage());
        }
    }



    /**
     * 人脸登录
     * @return
     */
    @ApiOperation(value = "人脸校验")
    @PostMapping("/face-check")
    public ApiRest<FaceCheckRespDTO> faceCheck(@RequestBody FaceLoginReqDTO reqDTO) {
        FaceCheckRespDTO respDTO = baseService.faceCheck(reqDTO.getBase64(), true);
        if(respDTO.isPass()){
            return super.success(respDTO);
        }
        throw new ServiceException(respDTO.getFailMsg());
    }


    @RequestMapping(value="/sms", method = RequestMethod.POST)
    @ApiOperation(value = "发送短信验证码")
    public ApiRest send(@RequestBody SendSmsReqDTO reqDTO) {

        // 场景
        Integer smsType = reqDTO.getSmsType();


        if(smsType == null){
            throw new ServiceException("短信应用场景未定义！");
        }


        // 用户注册
        if(SmsType.USER_REG.equals(smsType)){
            boolean exists = sysUserBindService.hasBind(LoginType.MOBILE, reqDTO.getMobile());
            if(exists){
                throw new ServiceException("手机号码已存在，请确认！");
            }
        }

        // 登录场景
        if(SmsType.USER_LOGIN.equals(smsType)){
            boolean exists = sysUserBindService.hasBind(LoginType.MOBILE, reqDTO.getMobile());
            if(!exists){
                throw new ServiceException("该手机号未绑定任何用户，请确认！");
            }
        }


        // 发送验证码 TODO 优化验证码
        boolean check = captchaService.checkCaptcha(reqDTO.getCaptchaKey(), reqDTO.getCaptchaValue());

        if(!check){
            throw new ServiceException("必须输入正确的图形验证码才能发送！");
        }

        // 发送短信验证码
        String code = RandomStringUtils.randomNumeric(6);
        Map<String,String> params = new HashMap<>();
        params.put("code", code);

        // 仅发短信通知
        List <MsgReceiverDTO> tos = new ArrayList<>();
        MsgReceiverDTO to1 = new MsgReceiverDTO();
        to1.setMobile(reqDTO.getMobile());
        tos.add(to1);

        // 存通知
        msgService.smsNotify(MsgTmpl.COMMON_SMS, tos, params);

        // 存入Redis
        redisService.set(SmsUtils.appendKey(reqDTO.getMobile()), code, SMS_EXPIRY);

        return success();
    }

}
